﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Common
{
    public class TrigonometricHelper
    {
        /// <summary>
        /// 折线的行进的当前步有没有出现调头现象
        /// </summary>
        /// <param name="tuple">向量状态有序对</param>
        /// <param name="pointStartInput">第一个点</param>
        /// <param name="pointEndInput">第二个点</param>
        /// <returns></returns>
        public static bool GetVectorByTwoPoint(LineState lineState, Point pointStartInput, Point pointEndInput) 
        {
            bool direction = pointEndInput.X - pointStartInput.X > 0;
            return !(direction == lineState.Poles);
        }
        /// <summary>
        /// 根据两点求直线方程斜率和截距
        /// </summary>
        /// <param name="pointStartInput">第一个点</param>
        /// <param name="pointEndInput">第二个点</param>
        /// <param name="linearEquation">直线</param>
        public static void SlopeAndIntercept(Point pointStartInput, Point pointEndInput, out LinearEquation linearEquation)
        {
            linearEquation = new LinearEquation();
            linearEquation.K = (pointStartInput.Y - pointEndInput.Y) / (pointStartInput.X - pointEndInput.X);
            linearEquation.B = pointEndInput.Y - linearEquation.K * pointEndInput.X;
        }

        /// <summary>
        /// 根据一条直线和垂直距离得到两条平行线
        /// </summary>
        /// <param name="lineState">直线行进方向</param>
        /// <param name="pointStartInput">第一个点</param>
        /// <param name="pointEndInput">第二个点</param>
        /// <param name="k">已知直线斜率</param>
        /// <param name="b">已知直线截距</param>
        /// <param name="verticalDistance">垂直距离</param>
        /// <param name="linearEquationPositive">第一条平行线</param>
        /// <param name="linearEquationNegative">第二条平行线</param>
        public static void GetTwoParallelLines(LineState lineState, Point pointStartInput, Point pointEndInput, double k, double b, double verticalDistance, out LinearEquation linearEquationPositive, out LinearEquation linearEquationNegative)
        {
            bool directionChanged = GetVectorByTwoPoint(lineState, pointStartInput, pointEndInput);
            if (directionChanged)//x轴方向改变
            {
                lineState.Poles = !lineState.Poles;
            }
            if (lineState.Poles)
            {
                linearEquationPositive = new LinearEquation(k, (b + Math.Sqrt(verticalDistance * verticalDistance + k * k * verticalDistance * verticalDistance)), lineState.Poles, pointStartInput, pointEndInput);
                linearEquationNegative = new LinearEquation(k, (b - Math.Sqrt(verticalDistance * verticalDistance + k * k * verticalDistance * verticalDistance)), lineState.Poles, pointStartInput, pointEndInput);
            }
            else
            {
                linearEquationPositive = new LinearEquation(k, (b - Math.Sqrt(verticalDistance * verticalDistance + k * k * verticalDistance * verticalDistance)), lineState.Poles, pointStartInput, pointEndInput);
                linearEquationNegative = new LinearEquation(k, (b + Math.Sqrt(verticalDistance * verticalDistance + k * k * verticalDistance * verticalDistance)), lineState.Poles, pointStartInput, pointEndInput);
            }
        }

        /// <summary>
        /// 合并正向重合直线
        /// </summary>
        /// <param name="linearEquations">直线集合</param>
        /// <returns>合并后的直线集合</returns>
        public static LinearEquation[] MergerLinearArray(LinearEquation[] linearEquations) 
        {
            int i = 0;
            List<LinearEquation> list=new List<LinearEquation>(linearEquations);
            while (i < list.Count-1)
            {
                //两直线重合
                if (list[i].K == list[i + 1].K)
                {
                    //同方向重合
                    if (list[i].LineState == list[i + 1].LineState)
                    {
                        list[i].PointEnd = list[i + 1].PointEnd;
                        list.RemoveAt(i + 1);
                    }
                    //反方向平行
                    else
                    {
                        i++;
                    }
                }
                else 
                {
                    i++;
                }
            }
            return list.ToArray();
        }
        
        /// <summary>
        /// 根据两直线，得到交点
        /// </summary>
        /// <param name="linearEquation1">第一条线</param>
        /// <param name="linearEquation2">第二条线</param>
        /// <param name="verticalDistance">垂直距离</param>
        /// <param name="isPositive">是否正极</param>
        /// <param name="points">点组集合</param>
        public static void GetPointOfIntersection(LinearEquation linearEquation1, LinearEquation linearEquation2, double verticalDistance, bool isPositive, ref List<Point> points) 
        {
            Point point = new Point();
            //判断两条线是重合还是相交
            if (linearEquation1.K != linearEquation2.K)//相交
            {
                point.X = (linearEquation2.B - linearEquation1.B) / (linearEquation1.K - linearEquation2.K);
                point.Y = (linearEquation2.B * linearEquation1.K - linearEquation1.B * linearEquation2.K) / (linearEquation1.K - linearEquation2.K);
                points.Add(point);
            }
            
            else//反方向平行,实际情况不可能发生，所以删除，备用
            {
                //Point ponintPositive;
                //Point pointNegative;
                //GetTwoPoints(linearEquation2.LineState,linearEquation2.PointStart, linearEquation2.PointEnd, verticalDistance, out ponintPositive, out pointNegative);
                //points.Add(isPositive ? ponintPositive : pointNegative);
                //points.Add(isPositive ? pointNegative : ponintPositive);
            }
        }

        /// <summary>
        /// 根据勾股定理推出
        /// 根据直角三角形一个锐角的tan值和一条斜边，得到直角三角形的两条直角边的长度
        /// </summary>
        /// <param name="k">直角三角形某锐角的tan值</param>
        /// <param name="c">直角三角形的斜边长度</param>
        /// <param name="a">直角三角形的直角边</param>
        /// <param name="b">直角三角形的另一条直角边</param>
        public static void PythagoreanTheorem(double k, double c, out double a, out double b)
        {
            b = Math.Sqrt((c * c * k * k    ) / (1 + k * k));
            a = Math.Sqrt(c*c-b*b);
        }

        /// <summary>
        /// 根据坐标系两点坐标求其一次函数的斜率k
        /// </summary>
        /// <param name="pointStartInput">第一个点</param>
        /// <param name="pointEndInput">第二个点</param>
        public static double GetSlope(Point pointStartInput, Point pointEndInput) 
        {
            return (pointStartInput.Y - pointEndInput.Y) / (pointStartInput.X - pointEndInput.X);
        }

        /// <summary>
        /// 根据输入的起始点和终点的坐标得到直线的一次函数的斜率，并且再根据垂直距离，计算出与该函数垂直并且相交于起始点的函数，距离起始点距离为垂直距离的两点的坐标
        /// </summary>
        /// <param name="pointStartInput">起始点</param>
        /// <param name="pointEndInput">终点</param>
        /// <param name="VerticalDistance">垂直距离</param>
        /// <param name="ponintPositive">所求结果点1</param>
        /// <param name="pointNegative">所求结果点2</param>
        public static void GetTwoPoints(bool lineState, Point pointStartInput, Point pointEndInput, double VerticalDistance, out Point ponintPositive, out Point pointNegative)
        {
            double k = GetSlope(pointStartInput, pointEndInput);
            double a = 0d;
            double b = 0d;
            if (k == 0)
            {
                if (lineState)
                {
                    ponintPositive = new Point(pointStartInput.X, pointStartInput.Y + VerticalDistance);
                    pointNegative = new Point(pointStartInput.X, pointStartInput.Y - VerticalDistance);
                }
                else
                {
                    ponintPositive = new Point(pointStartInput.X, pointStartInput.Y - VerticalDistance);
                    pointNegative = new Point(pointStartInput.X, pointStartInput.Y + VerticalDistance);
                }
            }
            else if (k == 1.0d / 0.0d || k == -(1.0d / 0.0d))//正负无穷大
            {
                if (lineState)
                {
                    ponintPositive = new Point(pointStartInput.X - VerticalDistance, pointStartInput.Y);
                    pointNegative = new Point(pointStartInput.X + VerticalDistance, pointStartInput.Y);
                }
                else
                {
                    ponintPositive = new Point(pointStartInput.X + VerticalDistance, pointStartInput.Y);
                    pointNegative = new Point(pointStartInput.X - VerticalDistance, pointStartInput.Y);
                }
            }
            else if (k > 0)
            {
                if (lineState)
                {
                    PythagoreanTheorem(k, VerticalDistance, out a, out b);
                    ponintPositive = new Point(pointStartInput.X + b, pointStartInput.Y - a);
                    pointNegative = new Point(pointStartInput.X - b, pointStartInput.Y + a);
                }
                else
                {
                    PythagoreanTheorem(k, VerticalDistance, out a, out b);
                    ponintPositive = new Point(pointStartInput.X - b, pointStartInput.Y + a);
                    pointNegative = new Point(pointStartInput.X + b, pointStartInput.Y - a);
                }
            }
            else
            {
                if (lineState)
                {
                    PythagoreanTheorem(k, VerticalDistance, out a, out b);
                    ponintPositive = new Point(pointStartInput.X + b, pointStartInput.Y + a);
                    pointNegative = new Point(pointStartInput.X - b, pointStartInput.Y - a);
                }
                else
                {
                    PythagoreanTheorem(k, VerticalDistance, out a, out b);
                    ponintPositive = new Point(pointStartInput.X - b, pointStartInput.Y - a);
                    pointNegative = new Point(pointStartInput.X + b, pointStartInput.Y + a);
                }
            }
        }
        /// <summary>
        /// 根据输入的起始点和终点的坐标得到直线的一次函数的斜率，并且再根据垂直距离，计算出与该函数垂直并且相交于起始点的函数，距离起始点距离为垂直距离的两点的坐标
        /// </summary>
        /// <param name="pointStartInput">起始点</param>
        /// <param name="pointEndInput">终点</param>
        /// <param name="VerticalDistance">垂直距离</param>
        /// <param name="pointsPositive">所求结果点1，加到掺入参数正极点组集合中</param>
        /// <param name="pointsNegative">所求结果点2，加到掺入参数负极点组集合中</param>
        public static void GetTwoPoints(bool lineState, Point pointStartInput, Point pointEndInput, double VerticalDistance, ref List<Point> pointsPositive, ref List<Point> pointsNegative)
        {
            double k = GetSlope(pointStartInput, pointEndInput);
            double a = 0d;
            double b = 0d;
            Point ponintPositive;
            Point pointNegative;
            if (k == 0)
            {
                if (lineState)
                {
                    ponintPositive = new Point(pointStartInput.X, pointStartInput.Y + VerticalDistance);
                    pointNegative = new Point(pointStartInput.X, pointStartInput.Y - VerticalDistance);
                }
                else 
                {
                    ponintPositive = new Point(pointStartInput.X, pointStartInput.Y - VerticalDistance);
                    pointNegative = new Point(pointStartInput.X, pointStartInput.Y + VerticalDistance);
                }
            }
            else if (k == 1.0d / 0.0d || k == -(1.0d / 0.0d))//正负无穷大
            {
                if (lineState)
                {
                    ponintPositive = new Point(pointStartInput.X - VerticalDistance, pointStartInput.Y);
                    pointNegative = new Point(pointStartInput.X + VerticalDistance, pointStartInput.Y);
                }
                else 
                {
                    ponintPositive = new Point(pointStartInput.X + VerticalDistance, pointStartInput.Y);
                    pointNegative = new Point(pointStartInput.X - VerticalDistance, pointStartInput.Y);
                }
            }
            else if (k > 0)
            {
                if (lineState)
                {
                    PythagoreanTheorem(k, VerticalDistance, out a, out b);
                    ponintPositive = new Point(pointStartInput.X - b, pointStartInput.Y + a);
                    pointNegative = new Point(pointStartInput.X + b, pointStartInput.Y - a);
                }
                else
                {
                    PythagoreanTheorem(k, VerticalDistance, out a, out b);
                    ponintPositive = new Point(pointStartInput.X + b, pointStartInput.Y - a);
                    pointNegative = new Point(pointStartInput.X - b, pointStartInput.Y + a);
                }
            }
            else
            {
                if (lineState)
                {
                    PythagoreanTheorem(k, VerticalDistance, out a, out b);
                    ponintPositive = new Point(pointStartInput.X + b, pointStartInput.Y + a);
                    pointNegative = new Point(pointStartInput.X - b, pointStartInput.Y - a);
                }
                else
                {
                    PythagoreanTheorem(k, VerticalDistance, out a, out b);
                    ponintPositive = new Point(pointStartInput.X - b, pointStartInput.Y - a);
                    pointNegative = new Point(pointStartInput.X + b, pointStartInput.Y + a);
                }
            }
            pointsPositive.Add(ponintPositive);
            pointsNegative.Add(pointNegative);
        }

        /// <summary>
        /// 根据输入点组集合和垂直距离，传出正负两组点集合
        /// </summary>
        /// <param name="lats">输入点组的纬度</param>
        /// <param name="lngs">输入点组的经度</param>
        /// <param name="VerticalDistance">垂直距离</param>
        /// <param name="pointsPositive">传出正极点组</param>
        /// <param name="pointsNegative">传出负极点组</param>
        public static string GetPoints(string[] lats, string[] lngs, double VerticalDistance)
        {
            //根据传入的经纬度数组，得到
            Point[] points = new Point[lats.Length];
            Point point;
            for (int i = 0; i < lats.Length; i++)
            {
                point = new Point(double.Parse(lngs[i]), double.Parse(lats[i]));
                points[i] = point;
            }
            LinearEquation[] linearEquations = new LinearEquation[points.Length - 1];
            LinearEquation[] linearEquationPositives = new LinearEquation[points.Length - 1];
            LinearEquation[] linearEquationNegatives = new LinearEquation[points.Length - 1];
            LineState lineState = new LineState();
            for (int i = 0; i < points.Length-1; i++)
            {
                if (i == 0)
                {
                    lineState.Poles = points[i+1].X - points[i].X >= 0;
                }
                //1.根据输入点组，找到直线方程式数组
                SlopeAndIntercept(points[i], points[i + 1], out linearEquations[i]);
                //2.找到每个直线的两条平行线数组
                GetTwoParallelLines(lineState, points[i], points[i + 1], linearEquations[i].K, linearEquations[i].B, VerticalDistance, out linearEquationPositives[i], out linearEquationNegatives[i]);
                linearEquations[i].LineState = lineState.Poles;
                linearEquationPositives[i].LineState = lineState.Poles;
                linearEquationNegatives[i].LineState = lineState.Poles;
            }
            //合并平行线组中重合的直线
            linearEquationPositives = MergerLinearArray(linearEquationPositives);
            linearEquationNegatives = MergerLinearArray(linearEquationNegatives);
            //正负极点组
            List<Point> pointsPositive = new List<Point>();
            List<Point> pointsNegative = new List<Point>();
            //加开始点
            GetTwoPoints(linearEquations[0].LineState, points[0], points[1], VerticalDistance, ref pointsPositive, ref pointsNegative);
            //前后个需要加上两个点
            for (int i = 1; i < linearEquationPositives.Length; i++)//从第二个点开始插入，一直到倒数第二个点
            {
                GetPointOfIntersection(linearEquationPositives[i - 1], linearEquationPositives[i], VerticalDistance, true, ref pointsPositive);
            }
            for (int i = 1; i < linearEquationNegatives.Length; i++)//从第二个点开始插入，一直到倒数第二个点
            {
                GetPointOfIntersection(linearEquationNegatives[i - 1], linearEquationNegatives[i], VerticalDistance, false, ref pointsNegative);
            }
            GetTwoPoints(linearEquations[linearEquations.Length-1].LineState, points[points.Length - 1], points[points.Length - 2], VerticalDistance, ref pointsPositive, ref pointsNegative);
            StringBuilder sb = new StringBuilder();
            //sb.Append("REGION ");//需要时候去掉！！！！！
            //sb.Append("Chull LATLON ");
            for (int i = 0; i < pointsPositive.Count - 1; i++)
            {
                sb.Append("POLY LATLON ");
                sb.Append(pointsPositive[i].Y.ToString());
                sb.Append(" ");
                sb.Append(pointsPositive[i].X.ToString());
                sb.Append(" ");
                sb.Append(pointsNegative[i].Y.ToString());
                sb.Append(" ");
                sb.Append(pointsNegative[i].X.ToString());
                sb.Append(" ");

                sb.Append(pointsNegative[i + 1].Y.ToString());
                sb.Append(" ");
                sb.Append(pointsNegative[i + 1].X.ToString());
                sb.Append(" ");
                sb.Append(pointsPositive[i + 1].Y.ToString());
                sb.Append(" ");
                sb.Append(pointsPositive[i + 1].X.ToString());
                if (i != pointsPositive.Count - 2)
                {
                    sb.Append(",");
                }
            }
            return sb.ToString();
        }

        /// <summary>
        /// 根据输入点组集合和垂直距离，传出正负两组点集合
        /// </summary>
        /// <param name="points">输入点组</param>
        /// <param name="VerticalDistance">垂直距离</param>
        /// <param name="pointList">传出所有点组，正负极点对排列</param>
        public static void GetPoints(Point[] points, double VerticalDistance, ref List<Point> pointList)
        {
            LinearEquation[] linearEquations = new LinearEquation[points.Length - 1];
            LinearEquation[] linearEquationPositives = new LinearEquation[points.Length - 1];
            LinearEquation[] linearEquationNegatives = new LinearEquation[points.Length - 1];
            LineState lineState = new LineState();
            for (int i = 0; i < points.Length - 1; i++)
            {
                if (i == 0)
                {
                    lineState.Poles = points[i + 1].X - points[i].X >= 0;
                }
                //1.根据输入点组，找到直线方程式数组
                SlopeAndIntercept(points[i], points[i + 1], out linearEquations[i]);
                //2.找到每个直线的两条平行线数组
                GetTwoParallelLines(lineState, points[i], points[i + 1], linearEquations[i].K, linearEquations[i].B, VerticalDistance, out linearEquationPositives[i], out linearEquationNegatives[i]);
                linearEquations[i].LineState = lineState.Poles;
                linearEquationPositives[i].LineState = lineState.Poles;
                linearEquationNegatives[i].LineState = lineState.Poles;
            }
            //合并平行线组中重合的直线
            linearEquationPositives = MergerLinearArray(linearEquationPositives);
            linearEquationNegatives = MergerLinearArray(linearEquationNegatives);
            //正负极点组
            List<Point> pointsPositive = new List<Point>();
            List<Point> pointsNegative = new List<Point>();
            Point pointStartPositive = new Point();
            Point pointStartNegative = new Point();
            //加开始点
            GetTwoPoints(linearEquations[0].LineState, points[0], points[1], VerticalDistance, out pointStartPositive, out pointStartNegative);
            pointList.Add(pointStartPositive);
            pointList.Add(pointStartNegative);
            //前后个需要加上两个点
            for (int i = 1; i < linearEquationPositives.Length; i++)//从第二个点开始插入，一直到倒数第二个点
            {
                GetPointOfIntersection(linearEquationPositives[i - 1], linearEquationPositives[i], VerticalDistance, true, ref pointList);
                GetPointOfIntersection(linearEquationNegatives[i - 1], linearEquationNegatives[i], VerticalDistance, false, ref pointList);
            }
            //加结束点
            Point pointEndPositive = new Point();
            Point pointEndNegative = new Point();
            GetTwoPoints(linearEquations[linearEquations.Length - 1].LineState, points[points.Length - 1], points[points.Length - 2], VerticalDistance, out pointEndPositive, out pointEndNegative);
            pointList.Add(pointEndPositive);
            pointList.Add(pointEndNegative);
        }
    }
    public class LinearEquation
    {
        private double _k;
        private double _b;
        private bool _lineState;
        private Point _pointStart;
        private Point _pointEnd;

        public LinearEquation() { }
        public LinearEquation(double k, double b)
        {
            _k = k;
            _b = b;
        }
        public LinearEquation(double k, double b, bool lineState)
        {
            _k = k;
            _b = b;
            _lineState = lineState;
        }
        public LinearEquation(double k, double b, bool lineState, Point pointStartInput, Point pointEndInput)
        {
            _k = k;
            _b = b;
            _lineState = lineState;
            _pointStart = pointStartInput;
            _pointEnd = pointEndInput;
        }

        public double K
        {
            get { return _k; }
            set { _k = value; }
        }

        public double B
        {
            get { return _b; }
            set { _b = value; }
        }

        public bool LineState
        {
            get { return _lineState; }
            set { _lineState = value; }
        }

        public Point PointStart
        {
            get { return _pointStart; }
            set { _pointStart = value; }
        }

        public Point PointEnd
        {
            get { return _pointEnd; }
            set { _pointEnd = value; }
        }
    }

    public class Point
    {
        private double _x;
        private double _y;
        public Point() { }
        public Point(double x, double y)
        {
            _x = x;
            _y = y;
        }

        public double X
        {
            get { return _x; }
            set { _x = value; }
        }

        public double Y
        {
            get { return _y; }
            set { _y = value; }
        }

        /// <summary>
        /// 测试用，正式使用的时候请删除
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("X的坐标是 ： ");
            sb.Append(_x.ToString());
            sb.Append("\r\n");
            sb.Append("Y的坐标是 ： ");
            sb.Append(_y.ToString());
            return sb.ToString();
        }
    }

    /// <summary>
    /// 是否延续上一步的x轴方向行进
    /// true为正，false为负
    /// </summary>
    public class LineState
    {
        private bool _poles;
        //true代表延X轴正方向行进，false代表延X轴负方向行进
        public bool Poles
        {
            get { return _poles; }
            set { _poles = value; }
        }
    }
}
